package com.eternal.data.ui;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.eternal.framework.utils.ConvertUtils;
import com.eternal.framework.utils.KLog;
import com.google.common.collect.Lists;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

public abstract class GraphBarView extends SurfaceView implements SurfaceHolder.Callback, GraphAdapter.Callback {
    private static final byte BAR_NUM = 8;

    private float graphHeight;
    private int standardPadding, graphWidth, barWidth, scrollerX, index, barDistance;

    private Paint labelPaint, barPaint, graphBoundsLinePaint, labelLinePaint, labelMadelPaint, scrollerLabelPaint,
            scrollerLabelTextPaint, scrollerLinePaint, graphLabelsLightTextPaint;
    private SurfaceHolder mHolder;
    private List<Integer> barNum;
    private List<Rect> barRect;
    private int[] size = new int[BAR_NUM];
    private char distance;
    private RectF scrollerLabelBounds;
    private Rect textBounds, scrollerLabelTextBounds;
    private SimpleDateFormat monthFormat, yearFormat;
    protected GraphAdapter adapter;
    private boolean scrollerOpen;
    private String scrollerLabelText;

    public GraphBarView(Context context) {
        this(context, null);
    }

    public GraphBarView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GraphBarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        getHolder().addCallback(this);
        init();
    }

    public void setAdapter(GraphAdapter adapter) {
        this.adapter = adapter;
        adapter.addCallback(this);
    }

    private void init() {
        standardPadding = ConvertUtils.sp2px(4.0f);
        barNum = Lists.newArrayListWithCapacity(8);
        barRect = Lists.newArrayListWithCapacity(8);
        textBounds = new Rect();
        scrollerLabelBounds = new RectF();
        scrollerLabelTextBounds = new Rect();
        monthFormat = new SimpleDateFormat("MMM dd", Locale.ENGLISH);
        yearFormat = new SimpleDateFormat("MMM yyyy", Locale.ENGLISH);
//        String pattern = ((SimpleDateFormat) SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM)).toPattern();
//        String regex;
//        if (pattern.contains("de")) {
//            regex = "[^Mn]*[Yy]+[^Mn]*";
//        } else {
//            regex = "[^DdMn]*[Yy]+[^DdMn]*";
//        }
//        monthFormat = new SimpleDateFormat(pattern.replaceAll(regex, ""), Locale.ENGLISH);\
        initPaint();
    }

    private void initPaint() {
        labelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        labelPaint.setStyle(Paint.Style.FILL);
        labelPaint.setTextSize(ConvertUtils.sp2px(12f));
        labelPaint.setColor(0xffffffff);

        barPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        barPaint.setStyle(Paint.Style.FILL);
//        barPaint.setShader(new LinearGradient(0, 0, 0,0,
//                new int[]{0xff128bec, 0xff1da9f1, 0xff29c9f7},null, Shader.TileMode.REPEAT));
        barPaint.setColor(0xff15BAFF);

        graphLabelsLightTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        graphLabelsLightTextPaint.setStyle(Paint.Style.FILL);
        graphLabelsLightTextPaint.setColor(0xffffffff);
        graphLabelsLightTextPaint.setTextSize(ConvertUtils.sp2px(10f));

        graphBoundsLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        graphBoundsLinePaint.setStyle(Paint.Style.STROKE);
        graphBoundsLinePaint.setColor(0xffffffff);
        graphBoundsLinePaint.setAlpha(81);
        graphBoundsLinePaint.setStrokeWidth(ConvertUtils.sp2px(1f));

        labelLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        labelLinePaint.setStyle(Paint.Style.STROKE);
        labelLinePaint.setColor(0x44888888);
        labelLinePaint.setStrokeWidth(ConvertUtils.sp2px(1f));

        labelMadelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        labelMadelPaint.setStyle(Paint.Style.STROKE);
        labelMadelPaint.setColor(0xffffffff);
        labelMadelPaint.setStrokeWidth(ConvertUtils.sp2px(1f));

        scrollerLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scrollerLabelPaint.setStyle(Paint.Style.FILL);
        scrollerLabelPaint.setColor(0xffffffff);

        scrollerLabelTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scrollerLabelTextPaint.setStyle(Paint.Style.FILL);
        scrollerLabelTextPaint.setColor(0xff15BAFF);
        scrollerLabelTextPaint.setTextSize(ConvertUtils.sp2px(12f));

        scrollerLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scrollerLinePaint.setStyle(Paint.Style.STROKE);
        scrollerLinePaint.setColor(0xffffffff);
        scrollerLinePaint.setStrokeWidth(2f);
    }

    @Override
    public void draw() {
        if(mHolder==null){
            KLog.e("holder is locked");
            return;
        }
        Canvas canvas = mHolder.lockCanvas();
        if (canvas == null) {
            KLog.e("canvas is locked");
            return;
        }
        canvas.save();
        canvas.drawRGB(0x14, 0x14, 0x14);
        canvas.translate(0, labelPaint.getTextSize() * 2.0f + standardPadding * 3.0f);
        canvas.drawText(String.format(Locale.ENGLISH, "MIN %s // MAX %s", formatMaxMin(getMin()),
                formatMaxMin(getMax())), 0, 0, labelPaint);
        //画内容顶部线
        canvas.translate(0, standardPadding);
        canvas.drawLine(0, 0, graphWidth, 0, graphBoundsLinePaint);
        //画标签
        drawLabel(canvas);
        for (Rect rect : barRect) {
            canvas.drawRect(rect, barPaint);
        }
        if(barRect.isEmpty()&&adapter.isInit()){
            String tip="No data is available for this period.";
            float x= (graphWidth-labelPaint.measureText(tip))/2;
            float y=(graphHeight+labelPaint.getTextSize())/2;
            canvas.drawText(tip, x, y, labelPaint);
        }
        canvas.translate(0, graphHeight);
        //画内容底部线
        canvas.drawLine(0, 0, graphWidth, 0, graphBoundsLinePaint);
        canvas.translate(0, -graphHeight);
        //如果双击了显示详情
        if (scrollerOpen) {
            canvas.drawLine(scrollerX, labelPaint.getTextSize() * -2f -
                            standardPadding * 2f, scrollerX,
                    graphHeight, scrollerLinePaint);
            float temp = scrollerLabelBounds.width() / 2;
            canvas.drawRoundRect(scrollerLabelBounds, temp, temp, scrollerLabelPaint);
            if (!TextUtils.isEmpty(scrollerLabelText)) {
                canvas.drawText(scrollerLabelText, scrollerLabelTextBounds.left,
                        scrollerLabelTextBounds.bottom, scrollerLabelTextPaint);
            }
        }

        canvas.translate(0, graphHeight+graphLabelsLightTextPaint.getTextSize()*2+labelPaint.getTextSize()*2+standardPadding*2+standardPadding);
//        canvas.translate(0, graphHeight + (labelPaint.getTextSize() + standardPadding) * 4);
        SimpleDateFormat format;
        if (adapter.timeDistance >= 31536000) {
            format = yearFormat;
        } else {
            format = monthFormat;
        }
        //画开始日期
        String temp = format.format(new Date(adapter.minTime * 1000L)).toUpperCase();
        labelPaint.getTextBounds(temp, 0, temp.length(), textBounds);
        canvas.drawText(temp, 0, 0, labelPaint);
        float minX = textBounds.width();
        //画结束日期
        temp = format.format(new Date((adapter.minTime + adapter.timeDistance) * 1000L)).toUpperCase();
        labelPaint.getTextBounds(temp, 0, temp.length(), textBounds);
        canvas.drawText(temp, getWidth() - textBounds.width() - 3, 0, labelPaint);
        float maxX = getWidth() - textBounds.width();
        //画底部的时间线
        int cy = 1 - textBounds.height() / 2;
        canvas.drawLine(minX + standardPadding * 2f, cy, maxX - standardPadding, cy, labelLinePaint);
        canvas.restore();

        mHolder.unlockCanvasAndPost(canvas);
    }

    private void drawLabel(Canvas canvas) {
        int x;
        String temp;
        canvas.translate(0, ConvertUtils.sp2px(2f));
        for (int i = 0; i < barNum.size(); i++) {
            temp = formatValue(barNum.get(i));
            x = barRect.get(i).left;
            canvas.drawText(temp, x + (barWidth - graphLabelsLightTextPaint.measureText(temp)) / 2f, graphHeight + graphLabelsLightTextPaint.getTextSize(),
                    graphLabelsLightTextPaint);
            canvas.drawLine(x + barWidth / 2f, graphHeight + graphLabelsLightTextPaint.getTextSize() + standardPadding,
                    x + barWidth / 2f, graphHeight + graphLabelsLightTextPaint.getTextSize() * 2 + standardPadding, labelMadelPaint);
            temp = formatValue(barNum.get(i) + distance);
            canvas.drawText(temp, x + (barWidth - graphLabelsLightTextPaint.measureText(temp)) / 2f, graphHeight + graphLabelsLightTextPaint.getTextSize() * 3 + standardPadding * 2,
                    graphLabelsLightTextPaint);
        }
        canvas.translate(0, -
                ConvertUtils.sp2px(2f));
    }

    @Override
    public void updatePath(int position) {
        Rect temp;
        barNum.clear();
        barRect.clear();
        if (adapter.length < 2) {
            return;
        }
        int min = (getMin() / 100 * 100);
        int value = getMax();
        for (int i = 0; i < BAR_NUM; i++) {
            size[i] = 0;
        }
        int distance = getMax() - min - 700;
        if (distance <= 0) {
            distance = 100;
            this.distance = 0;
        } else {
            distance = distance / 8;
            if (distance % 100 != 0) {
                distance = distance / 100 + 1;
            } else {
                distance = distance / 100;
            }
            this.distance = (char) (distance * 100);
            distance = (distance + 1) * 100;
        }

        for (int i = 0; min + i * distance <= value; ++i) {
            barNum.add(min + i * distance);
        }

        value = getItem(0).value;
        if (getItem(0).time <= adapter.minTime + adapter.timeDistance) {
            ++size[(value - min) / distance];
        }
        for (int i = 1; i < adapter.length - 1; ++i) {
            value = getItem(i).value;
            ++size[(value - min) / distance];
        }
        value = getItem(adapter.length - 1).value;
        if (getItem(adapter.length - 1).time >= adapter.minTime) {
            ++size[(value - min) / distance];
        }

        int maxNum = size[0];
        for (int i = 1; i < 8; i++) {
            if (maxNum < size[i]) {
                maxNum = size[i];
            }
        }
        while (size[0] == 0 && !barNum.isEmpty()) {
            System.arraycopy(size, 1, size, 0, barNum.size() - 1);
            barNum.remove(0);
        }
        while (!barNum.isEmpty() && size[barNum.size() - 1] == 0) {
            barNum.remove(barNum.size() - 1);
        }
//        for (int i=barNum.size()-1; i>=0; --i){
//            if(size[i]==0){
//                barNum.remove(i);
//            }else{
//                break;
//            }
//        }
        if (maxNum != 0 && barNum.size() != 0) {
            barDistance = graphWidth / barNum.size() - barWidth;
            int x = barDistance / 2;
            if (maxNum < 10) {
                ++maxNum;
            } else {
                maxNum = maxNum + maxNum / 10;
            }
            for (int i = 0; i < 8; ++i) {
                temp = new Rect(x, yFromNum(maxNum, size[i]), x + barWidth, ((int) graphHeight));
                x += barDistance + barWidth;
                barRect.add(temp);
            }
        } else {
            barNum.clear();
        }
        scrollerOpen = false;
    }

    private int yFromNum(int max, int now) {
        return ((int) (graphHeight - graphHeight * now / max));
    }

    public void hidePositionScroller(boolean _scrollerOpen) {
        scrollerOpen = _scrollerOpen;
        positionScroller(0);
    }

    private void positionScroller(float x) {
        if (scrollerOpen) {
            index = Math.round((x - (barDistance + barWidth) / 2f) / (barDistance + barWidth));
            KLog.i(String.format(Locale.ENGLISH, "scroller position %d for %f", index, x));
            if (index >= barNum.size() || size[index] == 0) {
                scrollerOpen = false;
            } else {
                int value = barNum.get(index);
                scrollerLabelText = String.format(Locale.ENGLISH, "%s—%s •%d",
                        formatValue(value), formatValue(value + distance), size[index]);
                scaleScroller();
            }
        }
        postDraw();
    }

    private void scaleScroller() {
        int scaledPadding = (int) (standardPadding * 1.5D);
        scrollerX = barRect.get(index).left + barWidth / 2;
        if (scrollerX < standardPadding || scrollerX > graphWidth - standardPadding || scrollerLabelText == null) {
            scrollerOpen = false;
            return;
        }
        scrollerLabelTextPaint.getTextBounds(scrollerLabelText, 0, scrollerLabelText.length(), scrollerLabelTextBounds);
        scrollerLabelTextBounds.offsetTo(((int) (scrollerX - scrollerLabelTextBounds.width() / 2f)),
                (int) ((labelPaint.getTextSize() + standardPadding) * -2f));
        if (scrollerLabelTextBounds.left < scaledPadding) {
            scrollerLabelTextBounds.offsetTo(scaledPadding, scrollerLabelTextBounds.top);
        }
        if (scrollerLabelTextBounds.right + scaledPadding > graphWidth) {
            scrollerLabelTextBounds.offsetTo(graphWidth - scrollerLabelTextBounds.width() - scaledPadding,
                    scrollerLabelTextBounds.top);
        }
        scrollerLabelBounds.set(
                scrollerLabelTextBounds.left - scaledPadding,
                scrollerLabelTextBounds.top - scaledPadding,
                scrollerLabelTextBounds.right + scaledPadding,
                scrollerLabelTextBounds.bottom + scaledPadding);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            scrollerOpen = true;
            positionScroller(event.getX());
        }
        return true;
    }

    private void postDraw() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                draw();
            }
        }).start();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mHolder = holder;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        graphHeight = height - labelPaint.getTextSize() * 7f - standardPadding * 7f;
        graphWidth = width;
        barWidth = width / 13;
        if (adapter != null) {
            postDraw();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mHolder = null;
    }

    abstract TimeValue getItem(int position);

    abstract int getMax();

    abstract int getMin();

    abstract String formatValue(int value);

    abstract String formatMaxMin(int value);

    public void setScrollerOpen(boolean scrollerOpen) {
        this.scrollerOpen = scrollerOpen;
    }
}
